
/**
 * @file:       trigger_scheduler.cpp
 * @author:     WubinWang
 * @contact:    wubinstu@163.com
 * @date:       2025-01-01
 * @license:    MIT License
 *
 * Copyright (c) 2025 WubinWang
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 *
 * DISCLAIMER:
 * 1. User acknowledges all application risks and consequences arising from usage
 *    of this software are exclusively borne by the user.
 * 2. The original author shall not be held liable for any direct, indirect,
 *    incidental, special, exemplary or consequential damages.
 * 3. No warranty of fitness for particular purpose or non-infringement is provided. */


#include "trigger_scheduler.hpp"

auto Trigger_Scheduler_ct::Task_Item_st::clean () noexcept -> void {
    this->_m_enabled        = false;
    this->_m_running        = false;
    this->_m_tid            = K_INVALID_TASK;
    this->_m_interval       = K_NO_INTERVAL;
    this->_m_deadline       = 0;
    this->_m_remaining      = K_REPEAT_INFINITE;
    this->_m_action         = nullptr;
    this->_m_condition      = nullptr;
    this->_m_condition_type = K_CONDITION_ET;
    this->_m_expire_action  = K_EXPIRE_JUST_KEEP;
    this->_m_condition_last = false;
}

Trigger_Scheduler_ct::Task_Builder_st & Trigger_Scheduler_ct::Task_Builder_st::enabled (bool value) noexcept {
    this->task._m_enabled = value;
    return *this;
}

Trigger_Scheduler_ct::Task_Builder_st & Trigger_Scheduler_ct::Task_Builder_st::running (bool value) noexcept {
    this->task._m_running = value;
    return *this;
}

Trigger_Scheduler_ct::Task_Builder_st & Trigger_Scheduler_ct::Task_Builder_st::tid (Task_ID_t value) noexcept {
    this->task._m_tid = value;
    return *this;
}

Trigger_Scheduler_ct::Task_Builder_st & Trigger_Scheduler_ct::Task_Builder_st::interval (Time_ANY_t value) noexcept {
    this->task._m_interval = value;
    return *this;
}

Trigger_Scheduler_ct::Task_Builder_st & Trigger_Scheduler_ct::Task_Builder_st::deadline (Time_ANY_t value) noexcept {
    this->task._m_deadline = value;
    return *this;
}

Trigger_Scheduler_ct::Task_Builder_st & Trigger_Scheduler_ct::Task_Builder_st::repeat (Task_Repeat_t value) noexcept {
    this->task._m_remaining = value;
    return *this;
}

Trigger_Scheduler_ct::Task_Builder_st & Trigger_Scheduler_ct::Task_Builder_st::action (Task_Main_ft fn) noexcept {
    this->task._m_action = std::move (fn);
    return *this;
}

Trigger_Scheduler_ct::Task_Builder_st & Trigger_Scheduler_ct::Task_Builder_st::zeroth_cycle (Task_Zeroth_Cycle_et value) noexcept {
    this->task._m_zeroth_cycle = value;
    return *this;
}

Trigger_Scheduler_ct::Task_Builder_st & Trigger_Scheduler_ct::Task_Builder_st::condition (Task_Condition_ft fn) noexcept {
    this->task._m_condition = std::move (fn);
    return *this;
}

Trigger_Scheduler_ct::Task_Builder_st & Trigger_Scheduler_ct::Task_Builder_st::condition_type (Task_Condition_Type_et value) noexcept {
    this->task._m_condition_type = value;
    return *this;
}

Trigger_Scheduler_ct::Task_Builder_st & Trigger_Scheduler_ct::Task_Builder_st::expire_action (Task_Expire_Action_et value) noexcept {
    this->task._m_expire_action = value;
    return *this;
}

Trigger_Scheduler_ct::Task_Builder_st & Trigger_Scheduler_ct::Task_Builder_st::condition_last (bool value) noexcept {
    this->task._m_condition_last = value;
    return *this;
}

Trigger_Scheduler_ct::Task_Item_st Trigger_Scheduler_ct::Task_Builder_st::build () const noexcept {
    return this->task;
}

auto Trigger_Scheduler_ct::taskValid (const Task_Item_st & _task) noexcept -> bool {
    if (_task._m_action == nullptr)  // 任务没有 action
        return false;

    if (_task._m_remaining == 0)  // 剩余触发次数为 0
        return false;

    if (_task._m_interval == K_NO_INTERVAL && _task._m_condition == nullptr)  // 无周期且无条件
        return false;

    if (_task._m_interval == K_NO_INTERVAL && _task._m_zeroth_cycle == K_ZEROTH_CYCLE_ON)  // 无周期且"一上来"触发
        return false;

    if (_task._m_remaining == K_REPEAT_INFINITE && _task._m_expire_action == K_EXPIRE_AUTO_CLEAN)  // 无限触发但自动清理
        return false;

    return true;
}

auto Trigger_Scheduler_ct::getTask (Task_ID_t _tid, bool _blocking) noexcept -> const Task_Item_st * {

    std::unique_lock<std::mutex> lock {this->_m_lock, std::defer_lock};

    if (!_blocking && !lock.try_lock ())
        return nullptr;
    else lock.lock ();

    for (auto & task : this->_m_tasks)
        if (task._m_tid == _tid)
            return &task;

    return nullptr;
}

auto Trigger_Scheduler_ct::enable () noexcept -> void {
    this->_m_enabled.store (true);
}

auto Trigger_Scheduler_ct::disable () noexcept -> void {
    this->_m_enabled.store (false);
}

auto Trigger_Scheduler_ct::registerTask (Task_Main_ft           _action,
                                         Time_ANY_t             _interval,
                                         Task_Repeat_t          _repeats,
                                         Task_Zeroth_Cycle_et   _zeroth_cycle,
                                         Task_Condition_ft      _condition,
                                         Task_Condition_Type_et _condition_type,
                                         Task_Expire_Action_et  _expire_action,
                                         bool                   _blocking) noexcept -> Task_ID_t {

    Task_Item_st new_task;
    new_task._m_tid            = this->_m_max_tid;
    new_task._m_enabled        = true;
    new_task._m_zeroth_cycle   = _zeroth_cycle;
    new_task._m_action         = std::move (_action);
    new_task._m_interval       = _interval;
    new_task._m_remaining      = _repeats;
    new_task._m_condition      = std::move (_condition);
    new_task._m_condition_type = _condition_type;
    new_task._m_expire_action  = _expire_action;
    new_task._m_deadline       = ((_zeroth_cycle == K_ZEROTH_CYCLE_ON) ? (this->_m_current_time) : (this->_m_current_time + _interval));
    new_task._m_condition_last = false;

    return this->registerTask (new_task, _blocking);
}

auto Trigger_Scheduler_ct::registerTask (Task_Item_st _task, bool _blocking) noexcept -> Task_ID_t {

    if (!this->_m_enabled.load ())
        return 0;

    if (!this->taskValid (_task))
        return 0;

    std::unique_lock<std::mutex> lock {this->_m_lock, std::defer_lock};

    if (!_blocking && !lock.try_lock ())
        return 0;
    else lock.lock ();

    _task._m_tid = this->_m_max_tid;
    this->_m_tasks.push_back (std::move (_task));
    return this->_m_max_tid++;
}

auto Trigger_Scheduler_ct::updateTask (Task_ID_t              _tid,
                                       Task_Main_ft           _action,
                                       Time_ANY_t             _interval,
                                       Task_Repeat_t          _repeats,
                                       Task_Zeroth_Cycle_et   _zeroth_cycle,
                                       Task_Condition_ft      _condition,
                                       Task_Condition_Type_et _condition_type,
                                       Task_Expire_Action_et  _expire_action,
                                       bool                   _blocking) noexcept -> bool {

    Task_Item_st new_task;
    new_task._m_tid            = _tid;
    new_task._m_enabled        = true;
    new_task._m_zeroth_cycle   = _zeroth_cycle;
    new_task._m_action         = std::move (_action);
    new_task._m_interval       = _interval;
    new_task._m_remaining      = _repeats;
    new_task._m_condition      = std::move (_condition);
    new_task._m_condition_type = _condition_type;
    new_task._m_expire_action  = _expire_action;
    new_task._m_deadline       = ((_zeroth_cycle == K_ZEROTH_CYCLE_ON) ? (this->_m_current_time) : (this->_m_current_time + _interval));
    new_task._m_condition_last = false;

    return this->updateTask (new_task, _blocking);
}

auto Trigger_Scheduler_ct::updateTask (const Task_Item_st & _task, bool _blocking) noexcept -> bool {

    if (!this->taskValid (_task))
        return false;

    std::unique_lock<std::mutex> lock {this->_m_lock, std::defer_lock};

    if (!_blocking && !lock.try_lock ())
        return false;
    else lock.lock ();

    for (auto & task : this->_m_tasks) {
        if (task._m_tid == _task._m_tid) {
            task = _task;
            return true;
        }
    }

    return false;
}

auto Trigger_Scheduler_ct::unregisterTask (Task_ID_t _tid, bool _blocking) noexcept -> bool {

    std::unique_lock<std::mutex> lock {this->_m_lock, std::defer_lock};

    if (!_blocking && !lock.try_lock ())
        return false;
    else lock.lock ();

    for (auto it = this->_m_tasks.begin (); it != this->_m_tasks.end (); ++it) {
        if (it->_m_tid == _tid) {
            // 任务正在运行, 理论上这种情况不可能出现
            // 因为任务正在运行的前提是 Trigger_Scheduler_ct::process () 正在被调用且获取了锁
            // 本函数能够执行到这里则说明不可能有任务正在运行
            if (it->_m_running)
                return false;

            this->_m_tasks.erase (it);
            return true;
        }
    }

    return false;
}

auto Trigger_Scheduler_ct::forceClear (bool _blocking) noexcept -> bool {

    std::unique_lock<std::mutex> lock {this->_m_lock, std::defer_lock};

    if (!_blocking && !lock.try_lock ())
        return false;
    else lock.lock ();

    this->_m_tasks.clear ();
    return true;
}

auto Trigger_Scheduler_ct::process (Time_ANY_t _current_time) noexcept -> void {

    if (!this->_m_enabled.load ())
        return;

    if (_current_time != 0)
        this->_m_current_time = _current_time;

    std::unique_lock<std::mutex> lock {this->_m_lock};

    for (auto it = this->_m_tasks.begin (); it != this->_m_tasks.end ();) {
        if (!it->_m_enabled || it->_m_running) {
            if (!it->_m_enabled && it->_m_expire_action == K_EXPIRE_AUTO_CLEAN)
                it = this->_m_tasks.erase (it);
            else ++it;
            continue;
        }

        bool condition_met = false;
        if (it->_m_condition) {
            bool current_cond = it->_m_condition ();
            if (it->_m_condition_type == K_CONDITION_ET) {
                if (current_cond && !it->_m_condition_last)
                    condition_met = true;
                it->_m_condition_last = current_cond;
            } else if (it->_m_condition_type == K_CONDITION_LT)
                condition_met = current_cond;
        }

        bool time_met = (it->_m_interval == K_NO_INTERVAL) ||                          // 周期为 0, 表示无固定周期, 直接认定 time_met
                        (timePassedOrEqual (it->_m_deadline, this->_m_current_time));  // 或者有固定周期并且时间达到周期

        if ((it->_m_condition ? condition_met : true) && time_met) {
            it->_m_running  = true;
            it->_m_deadline = this->_m_current_time + it->_m_interval;
            if (it->_m_remaining != K_REPEAT_INFINITE)
                it->_m_remaining--;

            it->_m_action ();

            it->_m_running = false;
            if (it->_m_remaining == 0)
                it->_m_enabled = false;
        }
        ++it;
    }
}
